﻿///////////////////////////////////////////////////////////////
//                                                           //
// Code from Arcanea Project.                                //
//                                                           //
// Please include this license part to every reproduction    //
//   of code.                                                //
//                                                           //
// This code is under Ms-RL license                          //
//                                                           //
// Please read futher information about the license,         //
//   the project (news and updates) and the                  //
//   contributors on the address :                           //
//     http://www.codeplex.com/Arcanea/                      //
//                                                           //
///////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
using Arcanea.Mediator.FullClasses;
using Arcanea.BaseEngine.Interfaces;
using Arcanea.Mediator.Exceptions;
using Arcanea.BaseEngine.Generics;

namespace Arcanea.BaseEngine
{
    /// <summary>
    /// Classe fournissant les fonctions de
    /// 
    /// </summary>
    public sealed class ProcessEngine
    {
        #region Public Select

        /// <summary>
        /// Selectionne des objets en base selon une contrainte.
        /// </summary>
        /// <param name="constraint">Contrainte a respecter</param>
        /// <param name="sqlAccess">Connection Sql</param>
        /// <returns>Liste des paquets selectionnes</returns>
        public static IList<FullPacket> SelectAll(FullPacket constraint, IBaseAccess sqlAccess)
        {
            StringBuilder requete = new StringBuilder();
            requete.Append("SELECT * FROM ");
            requete.Append(constraint.Name);

            StringBuilder condition = BuildWhere(constraint);
            requete.Append(condition.Length == 0 ? "" : " WHERE " + condition);

            IBaseResultSet rs = sqlAccess.ExecuteQuery(requete.ToString());

            return ExtractResults(rs, constraint);
        }

        public static FullPacket SelectOneException(FullPacket constraint, IBaseAccess sqlAccess)
        {
            return SelectOne(constraint, sqlAccess, true);
        }

        public static FullPacket SelectOne(FullPacket constraint, IBaseAccess sqlAccess)
        {
            return SelectOne(constraint, sqlAccess, false);
        }

        #endregion

        #region Public Update

        /// <summary>
        /// Permet l update de plusieurs paquet selon un schema.
        /// </summary>
        /// <param name="constraint">Contrainte aue doit respecter le paquet a modifier</param>
        /// <param name="modifier">Pattern de modification du paquet</param>
        /// <param name="sqlAccess">Acces Sql fournissant la connection</param>
        /// <returns>Le nombre d enregistrement impacte</returns>
        public static int UpdateAll(FullPacket constraint, FullPacket modifier, IBaseAccess sqlAccess)
        {
            StringBuilder requete = new StringBuilder();
            requete.Append("UPDATE ");
            requete.Append(constraint.Name);
            StringBuilder condition = BuildWhere(constraint);
            StringBuilder setter = BuildWhere(modifier);
            if (setter.Length == 0)
                throw new InvalidDatabaseEngineUtilisationException("No set parameters in Update.");

            requete.Append(" SET ");
            requete.Append(setter);
            requete.Append(condition.Length == 0 ? "" : " WHERE " + condition);

            return sqlAccess.ExecuteSimple(requete.ToString());

        }

        #endregion

        #region Public Delete

        /// <summary>
        /// Delete les enregistrements dans la base selon la contrainte passe en argument
        /// </summary>
        /// <param name="constraint">Contrainte a respecter pour la deletion</param>
        /// <param name="sqlAccess">Acces Sql fournissant la connection</param>
        /// <returns>Le nombre d element impacte</returns>
        public static int DeleteAll(FullPacket constraint, IBaseAccess sqlAccess)
        {
            StringBuilder requete = new StringBuilder();
            requete.Append("DELETE FROM ");
            requete.Append(constraint.Name);
            StringBuilder condition = BuildWhere(constraint);

            requete.Append(condition.Length == 0 ? "" : " WHERE " + condition);

            return sqlAccess.ExecuteSimple(requete.ToString());
        }

        #endregion

        #region Public insert

        /// <summary>
        /// Insert dqns une base un "paquet"
        /// </summary>
        /// <param name="packet">Paquet a inserer</param>
        /// <param name="sqlAccess">Classe fournissant l acces Sql</param>
        /// <returns>Nombre d element insere</returns>
        public static int InsertAll(FullPacket packet, IBaseAccess sqlAccess)
        {
            StringBuilder requete = new StringBuilder();
            requete.Append("INSERT INTO ");
            requete.Append(packet.Name);
            StringBuilder allFieldsNames = BuildFieldList(packet);
            StringBuilder allFieldsValues = BuildFieldValues(packet);
            if (allFieldsNames.Length == 0 || allFieldsValues.Length == 0)
                throw new InvalidDatabaseEngineUtilisationException("No fields defined in the class");

            requete.Append(" ");
            requete.Append("(" + allFieldsNames + ")");
            requete.Append(" VALUES ");
            requete.Append("(" + allFieldsValues + ")");
            
            return sqlAccess.ExecuteSimple(requete.ToString());
        }

        #endregion

        #region Private static options

        private static StringBuilder BuildWhere(FullPacket constraint)
        {
            StringBuilder condition = new StringBuilder();


            for (int i = 0; i < constraint.FieldsArraySize; i++)
            {
                if (constraint.BinaryFrame.values[i] != null)
                {
                    if (condition.Length != 0)
                        condition.Append(" AND ");

                    condition.Append(constraint.FieldsDescription[i].Name);
                    condition.Append(" = '");
                    condition.Append(constraint.BinaryFrame.values[i]);
                    condition.Append("'");

                }
            }
            return condition;
        }

        private static StringBuilder BuildFieldValues(FullPacket constraint)
        {
            StringBuilder condition = new StringBuilder();

            for (int i = 0; i < constraint.FieldsArraySize; i++)
            {
                if (constraint.BinaryFrame.values[i] != null)
                {
                    if (condition.Length != 0)
                        condition.Append(" , ");

                    condition.Append("'");
                    condition.Append(constraint.BinaryFrame.values[i]);
                    condition.Append("'");

                }
            }
            return condition;
        }

        private static StringBuilder BuildFieldList(FullPacket constraint)
        {
            StringBuilder condition = new StringBuilder();


            for (int i = 0; i < constraint.FieldsArraySize; i++)
            {
                if (constraint.BinaryFrame.values[i] != null)
                {
                    if (condition.Length != 0)
                        condition.Append(" , ");

                    condition.Append(constraint.FieldsDescription[i].Name);
                }
            }
            return condition;
        }

        private static List<FullPacket> ExtractResults(IBaseResultSet rs, FullPacket constraint)
        {
            List<FullPacket> result = new List<FullPacket>();

            while (rs.NextResult())
            {
                FullPacket temp = new FullPacket(constraint.BinaryFrame, constraint.XmlFrame);
                for (int i = 0; i < constraint.FieldsArraySize; i++)
                {
                    temp.BinaryFrame.values[i] = rs.GetObject(i, temp.XmlFrame.Fields[i].SystemType);
                }
                result.Add(temp);
            }
            rs.Close();
            return result;
        }

        private static FullPacket ExtractOneResults(IBaseResultSet rs, FullPacket constraint, bool throwException)
        {
            List<FullPacket> result = new List<FullPacket>();
            while (rs.NextResult())
            {
                FullPacket temp = new FullPacket(constraint.BinaryFrame, constraint.XmlFrame);
                for (int i = 0; i < constraint.FieldsArraySize; i++)
                {
                    temp.BinaryFrame.values[i] = rs.GetObject(i, temp.XmlFrame.Fields[i].SystemType);
                }
                result.Add(temp);
                if (result.Count != 1)
                {
                    if (throwException)
                        throw new DatabaseResultNumberException("There is not only one result in the Select/Delete/Update");
                    else
                        break;
                }

            }
            rs.Close();

            if (result.Count != 1 && throwException)
                throw new DatabaseResultNumberException("There is not only one result in the Select/Delete/Update : " + result.Count);

            if (result.Count >= 1)
                return result[0];
            else
                return null;
        }

        private static FullPacket SelectOne(FullPacket constraint, IBaseAccess sqlAccess, bool throwException)
        {
            StringBuilder requete = new StringBuilder();
            requete.Append("SELECT * FROM ");
            requete.Append(constraint.Name);

            StringBuilder condition = BuildWhere(constraint);
            requete.Append(condition.Length == 0 ? "" : " WHERE " + condition);

            IBaseResultSet rs = sqlAccess.ExecuteQuery(requete.ToString());

            return ExtractOneResults(rs, constraint, throwException);
        }

        #endregion
    }
}
